import 'dart:convert';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:image_picker/image_picker.dart';
import 'package:get/get.dart';

import '../../../common/index.dart';
import '../../apps/cms/cms_app/index.dart';
import '../../apps/cms/cms_document/index.dart';
import '../create_form/index.dart';
import '../preview_image/index.dart';
import '../video_player/index.dart';
import 'index.dart';

class ProcessWebviewController extends GetxController implements JsNavigationInterface {
  ProcessWebviewController();

  final state = ProcessWebviewState();
  final _eventBus = EventBus();
  final channel = O2FlutterMethodChannelUtils();
  // webview控件的控制器
  // final GlobalKey webViewKey = GlobalKey();
  InAppWebViewController? webviewController;
  // webview 通用方法
  final webviewHelper = WebviewHelper();
  // 安装转化js
  var isInstallJsName = false;
    // 拍照 图片选择
  final ImagePicker _imagePicker = ImagePicker();

  // 工作对象
  Work? _work;

  // cms 创建文档使用的对象
  CmsAppData? _app;
  CmsCategoryData? _category;

  // 防止重复关闭
  bool alreadyClose = false;


  /// 在 widget 内存中分配后立即调用。
  @override
  void onInit() {
    OLogger.d('执行了   onInit ');
    super.onInit();
  }

  /// 在 onInit() 之后调用 1 帧。这是进入的理想场所
  @override
  void onReady() {
    OLogger.d('执行了   onReady');
    var map = Get.arguments;
    if (map != null) {
      state.title = map["title"] ?? "";
      OLogger.d("设置标题 title: ${state.title} ");
      final id = map["workOrWorkCompletedId"];
      final draft = map['draft'];
      if (id != null && id is String && id.isNotEmpty) {
        loadWorkOrWorkcompleted(id);
        initWorkUrl(id);
      } else if (draft != null && draft is ProcessDraftWorkData) {
        initDraftUrl(draft);
      }
    }
    super.onReady();
  }

  /// 在 [onDelete] 方法之前调用。
  @override
  void onClose() {
    OLogger.d('onClose webController : ${webviewController == null}');
    if (alreadyClose) {
      super.onClose();
      return;
    }
    alreadyClose = true;
    _executeCallbackJs("layout.app.appForm.finishOnMobile", null);
    OLogger.d('执行了新建检查！在 close');
    super.onClose();
  }

  /// dispose 释放内存
  @override
  void dispose() {
     OLogger.d('dispose webController : ${webviewController == null}');
    super.dispose();
  }


  @override
  void closeWindow() {
    _workNewCheckAndClose();
  }
  
  @override
  void goBack() {
    OLogger.d('执行了 jsapi goBack ');
    tapBackBtn();
  }
  
  @override
  void setNavigationTitle(String title) {
    OLogger.d('执行了 jsapi setNavigationTitle $title ');
    state.title = title;
  }


  ///
  /// 点击appbar 返回按钮
  ///
  void tapBackBtn() async {
    if (await webviewController?.canGoBack() == true) {
      webviewController?.goBack();
    } else {
      _workNewCheckAndClose(); // js 执行后会回调关闭窗口
    }
  }
  /// 新建检查
  void _workNewCheckAndClose() async {
    if (alreadyClose) {
      return;
    }
    alreadyClose = true;
    _executeCallbackJs("layout.app.appForm.finishOnMobile", null);
    OLogger.d('执行了新建检查！');
    _eventBus.emit(EventBus.processWorkCloseMsg, 'close');
    Get.back();
  }

  ///
  /// 草稿模式
  /// 
  void initDraftUrl(ProcessDraftWorkData draft) async {
    String  draftJson = json.encode(draft.toJson());
    var url = O2ApiManager.instance.getProcessDraftUrl(draftJson) ?? '' ;
    if (url.isNotEmpty) {
      openUrl(url);
    }
  }

  ///
  /// 初始化工作url
  ///
  void initWorkUrl(String workOrWorkCompletedId) async {
    var url = O2ApiManager.instance.getWorkUrl(workOrWorkCompletedId) ?? "";
    if (url.isNotEmpty) {
      openUrl(url);
    }
   
  }

  void openUrl(String url) async {
    final uurl = Uri.parse(url);
    var host = O2ApiManager.instance.getWebHost();
    var domain = uurl.host;
    // if (host.isNotEmpty && host.isIPv4) {
    //   domain = host; // ip地址
    // } else {
    //   domain = url.getHostFromUrl();
    // }
    var tokenName = O2ApiManager.instance.tokenName;
    var token = O2ApiManager.instance.o2User?.token ?? '';
    OLogger.d(
        "加载webview cookie，url: $url domain: $domain tokenName: $tokenName token: $token");
    CookieManager cookieManager = CookieManager.instance();
    await cookieManager.setCookie(
        url: uurl,
        name: tokenName,
        value: token,
        domain: (domain.isEmpty ? host : domain));
    state.url = url;
    OLogger.d("打开网址： $url");
  }

  ///
  /// 加载js通道
  ///
  void setupWebviewJsHandler(InAppWebViewController c) async {
    webviewController = c;
    webviewController?.addJavaScriptHandler(
        handlerName: O2.webviewChannelNameCommonKey,
        callback: (msgs) {
          OLogger.d(
              "js 通信， name: ${O2.webviewChannelNameCommonKey} msg: $msgs");
          if (msgs.isNotEmpty) {
            String msg = msgs[0] as String? ?? "";
            jsChannelMessageReceived(msg);
          }
        });
    webviewHelper.setupWebviewJsHandler(c);
    webviewHelper.setupJsNavigationInterface(this);
  }
  /// webview 加载进度
  void progressChanged(InAppWebViewController c, int p) {
    OLogger.d("o2Webview process progress: $p");
    // 这里把  inappwebview的 js handler 方式修改成 我们自定义的
    if (p == 100 && !isInstallJsName) {
      isInstallJsName = true;
      // o2android
      var js = '''
            if (window.flutter_inappwebview && window.flutter_inappwebview.callHandler) {
              window.o2android = {};
              window.o2android.postMessage = function(message){
                window.flutter_inappwebview.callHandler('o2android', message);
              };
            }
          ''';
      c.evaluateJavascript(source: js);
      OLogger.i("执行o2android转化js完成。。。");
      webviewHelper.changeJsHandlerFunName(c);
    }
  }

  void loadWorkOrWorkcompleted(String id) async {
    var info = await ProcessSurfaceService.to.getWorkOrWorkcompletedWithId(id);
    if (info != null && info.work != null) {
      OLogger.d("请求获取work对象成功!");
      _work = info.work;
    }
  }

  ///
  /// 接收js通道返回数据
  ///   h5上调用js执行flutter这边的原生方法
  ///
  // void jsChannelMessageReceived(JavascriptMessage message) {
  // if (message.message.isNotEmpty) {
  void jsChannelMessageReceived(String message) {
    OLogger.d("h5执行原生方法，message: $message");
    if (message.isEmpty) {
      return;
    }
    var jsMessage = JsMessage.fromJson(O2Utils.parseStringToJson(message));
    switch (jsMessage.type) {
      case "closeWork":
        _workNewCheckAndClose();
        break;
      case 'openO2Work':
        _openWork(jsMessage.data);
        break;
      case "appFormLoaded":
        _appFormLoaded(jsMessage.data);
        break;
      case 'createO2CmsDocument':
        _createO2CmsDocument(jsMessage.data);
        break;
      case 'openO2CmsApplication':
        _openO2CmsApplication(jsMessage.data);
        break;
      case 'openO2CmsDocument':
        _openO2CmsDocument(jsMessage.data);
        break;
      case "downloadAttachment":
        _downloadAttachment(jsMessage.data);
        break;
      case "uploadAttachment":
        _uploadAttachment(jsMessage.data);
        break;
      case "uploadAttachmentForDatagrid":
        _uploadAttachment(jsMessage.data, forGrid: true);
         break;
      case "replaceAttachment":
        _replaceAttachment(jsMessage.data);
        break;
      case "replaceAttachmentForDatagrid":
        _replaceAttachment(jsMessage.data, forGrid: true);
        break;
      case "openDocument":
        _openDocument(jsMessage.data);
        break;
      case "uploadImage2FileStorage": // imageclipper 图片上传控件
        _uploadImage2FileStorage(jsMessage.data);
        break;
      default:
        break;
    }
    _executeCallbackJs(jsMessage.callback, null);
  }



  ///
  /// 创建文档
  /// * 创建文档 目前只有 column 和 category 有效果
  /* {
            "column" : column, //（string）可选，内容管理应用（栏目）的名称、别名或ID
            "category" : category, //（string）可选，要创建的文档所属的分类的名称、别名或ID
            "data" : data, //（json object）可选，创建文档时默认的业务数据
            "identity" : identity, //（string）可选，创建文档所使用的身份。如果此参数为空，且当前人有多个身份的情况下，会弹出身份选择对话框；否则使用默认身份。
            "callback" : callback, //（funcation）可选，文档创建后的回调函数。
            "target" : target, //（boolean）可选，为true时，在当前页面打开创建的文档；否则打开新窗口。默认false。
            "latest" : latest, //（boolean）可选，为true时，如果当前用户已经创建了此分类的文档，并且没有发布过，直接调用此文档为新文档；否则创建一个新文档。默认true。
            "selectColumnEnable" : selectColumnEnable, //（boolean）可选，是否可以选择应用和分类进行创建文档。有category参数时为默认false,否则默认为true。
            "ignoreTitle" : ignoreTitle //（boolean）可选，值为false时，创建的时候需要强制填写标题，默认为false。
            "restrictToColumn" : restrictToColumn //（boolean）可选，值为true时，会限制在传入的栏目中选择分类，默认为false。
            } */
  ///
  void _createO2CmsDocument(Map<String, dynamic>? data) async {
    OLogger.d('data: $data');
    _app = null;
    _category = null;
    if (data != null) {
      var categoryId = data['category'];
      if (categoryId != null && categoryId is String && categoryId.isNotEmpty) {
        // 根据分类id查询 分类和应用
        _category = await CmsAssembleControlService.to.getCategory(categoryId);
        if (_category != null && _category?.appId != null && _app == null) {
          _app = await CmsAssembleControlService.to.getApp(_category!.appId!);
        }
      } else {
        var appId = data['column'];
        if (appId != null && appId is String && appId.isNotEmpty) {
          // 只有应用id的情况 弹出分类选择器
          _app = await CmsAssembleControlService.to
              .getAppCanPublishCategories(appId);
          if (_app != null &&
              _app?.wrapOutCategoryList != null &&
              _app!.wrapOutCategoryList!.isNotEmpty) {
            var cList = _app!.wrapOutCategoryList!;
            if (cList.length == 1) {
              _category = cList[0];
            } else {
              _showCmsCategoryChoose(cList);
              return;
            }
          }
        }
      }
    }
    // 传入参数不足，选择分类
    if (_app == null || _category == null) {
      var category = await Get.toNamed(O2OARoutes.appCmsCategoryPicker);
      if (category != null && category is CmsCategoryData) {
        OLogger.d('选择了分类： ${category.toJson()}');
        _app = category.withApp;
        _category = category;
      }
    }
    _startCreateDocument();
  }
   /// 选择分类
  void _showCmsCategoryChoose(List<CmsCategoryData> list) {
    final c = Get.context;
    if (c != null) {
      O2UI.showBottomSheetWithCancel(
          c,
          list
              .map((e) => ListTile(
                    onTap: () {
                      Navigator.pop(c);
                      _category = e;
                      _startCreateDocument();
                    },
                    title: Align(
                      alignment: Alignment.center,
                      child: Text(e.categoryName ?? '',
                          style: Theme.of(c).textTheme.bodyMedium),
                    ),
                  ))
              .toList());
    }
  }
  ///
  /// 参数查询完成后开始创建文档
  /// _app、_category 都赋值后
  /// 
  void _startCreateDocument() async {
    if (_app == null || _category == null) {
      Loading.toast('cms_create_document_no_args'.tr);
      return;
    }
    OLogger.d('创建文档，app：${_app?.toJson()}');
    OLogger.d('创建文档，category：${_category?.toJson()}');
    final config = _app?.config;
    bool ignoreTitle = false; // 是否忽略标题
    bool latest = true; // 是否查询草稿
    if (config != null && config.isNotEmpty) {
      final map = O2Utils.parseStringToJson(config);
      final ig = map['ignoreTitle'];
      if (ig != null && ig is bool) {
        ignoreTitle = ig;
      }
      final la = map['latest'];
      if (la != null && la is bool) {
        latest = la;
      }
    }
    if (latest) {
      var drafts = await CmsAssembleControlService.to.listDocumentDraft(_category!.id!);
      if (drafts != null && drafts.isNotEmpty) {
        OLogger.d('有草稿。。。。');
        CmsDocumentPage.open(drafts[0].id!,
            title: drafts[0].title ?? '', options: { "readonly": false });
        return;
      }
    }
    // 打开创建工作页面
    CreateFormPage.startCmsDoc(ignoreTitle, category: _category!);

  }
  
  /// 打开信息中心
  /// appId: String, title: String
  Future<void> _openO2CmsApplication(Map<String, dynamic>? data) async {
    if (data != null && data['appId'] !=null && data['appId'] is String) {
      var list = await CmsAssembleControlService.to.listAppWithCategoryUserCanView();
      if (list != null) {
        var app = list.firstWhereOrNull((element) => element.id == data['appId'] ||  element.appAlias == data['appId'] ||  element.appName == data['appId']);
        if (app != null) {
          CmsAppPage.open(app); // 打开对应的
          return;
        }
      }
    }
    // 没有参数 直接打开cms 
    Get.toNamed(O2OARoutes.appCms);
  }

  /// 打开信息文档
  /// docId: String, title: String, options: Map<String, dynamic>?
  void _openO2CmsDocument(Map<String, dynamic>? data) {
    if (data != null) {
      String docId = data['docId'] ?? '';
      if (docId.isNotEmpty) {
        CmsDocumentPage.open(docId,
            title: data['title'] ?? '', options: data['options']);
      }
    }
  }
 

  /// 选择图片 或者 拍照
  /// 图片控件使用
  Future<void> _pickImageByType(int type, Map<String, dynamic> data) async {
    XFile? file;
    if (type == 0) {
      // 相册
      file = await _imagePicker.pickImage(source: ImageSource.gallery);
    } else if (type == 1) {
      // 拍照
      file = await _imagePicker.pickImage(source: ImageSource.camera);
    }
    if (file != null) {
       // var mwfId: String = "",//控件id
      // var callback: String = "",//回调函数
      // var referencetype: String = "",//上传文件服务器的业务类型名称
      // var reference: String = "",//关联业务id
      // var fileId: String = ""//上传返回的文件id
      String referencetype = data['referencetype'] ?? '';
      String reference = data['reference'] ?? '';
      String callback = data['callback'] ?? '';
      if (referencetype.isEmpty || reference.isEmpty || callback.isEmpty) {
        Loading.showError('args_error'.tr);
        return;
      }
      Loading.show();
      var id = await FileAssembleService.to.uploadImageWithReferencetype(referencetype, reference, File(file.path));
      if (id != null && id.id != null) {
        data['fileId'] = id.id;
        final back = json.encode(data);
        final js = '$callback(\'$back\')';
        OLogger.d('执行js ：$js');
        await webviewController?.evaluateJavascript(source: js);
        Loading.dismiss();
      }
    }
  }

  ///
  /// 图片上传控件
  /// 
  void _uploadImage2FileStorage(Map<String, dynamic>? data) {
    final myContext = Get.context;
    if (data != null && myContext != null) {
      O2UI.showBottomSheetWithCancel(myContext, [
              ListTile(
                onTap: () {
                  Navigator.pop(myContext);
                  _pickImageByType(0, data);
                },
                title: Align(
                  alignment: Alignment.center,
                  child: Text('album'.tr,
                      style: Theme.of(myContext)
                          .textTheme
                          .bodyMedium),
                ),
              ),
              const Divider(height: 1),
              ListTile(
                onTap: () {
                  Navigator.pop(myContext);
                  _pickImageByType(1, data);
                },
                title: Align(
                  alignment: Alignment.center,
                  child: Text('take_photo'.tr,
                      style: Theme.of(myContext)
                          .textTheme
                          .bodyMedium),
                ),
              ),
            ]);
    }
  }

  ///
  /// 上传附件
  /// 
  void _uploadAttachment(Map<String, dynamic>? data, {bool forGrid = false}) async {
    if (data != null && _work != null) {
       String site = data["site"] ?? "";
       String param = data["param"] ?? "";
       OLogger.d('上传附件 site $site  , param $param');
       O2Utils.pickerFileOrImage((paths) {
        _uploadAttachmentBegin(paths, site, param, forGrid);
       }, allowMultiple: true);
    }
  }
  void _uploadAttachmentBegin(List<String?> paths, String site, String param , bool forGrid) async {
    if (paths.isEmpty) {
      return;
    }
    Loading.show();
    OLogger.d('选择了文件：$paths');
    int errorNumber = 0;
    for (var element in paths) {
      if (element != null && element.isNotEmpty) {
        var resId = await ProcessSurfaceService.to.uploadAttachment(_work!.id!, site, File(element));
        if (resId != null && resId.id != null) {
          if (forGrid) {
            webviewController?.evaluateJavascript(source: "layout.app.appForm.uploadedAttachmentDatagrid(\"$site\", \"${resId.id}\", \"$param\")");
          } else {
            webviewController?.evaluateJavascript(source: "layout.app.appForm.uploadedAttachment(\"$site\", \"${resId.id}\")");
          }
          OLogger.d('上传附件成功 ${resId.id} ');
        } else {
          errorNumber++;
        }
      }
    }
    if (errorNumber > 0) {
      Loading.showError(
          'process_attachment_upload_error'.trArgs(['$errorNumber']));
    } else {
      Loading.dismiss();
    }

  }


  ///
  /// 替换附件
  /// 
  void _replaceAttachment(Map<String, dynamic>? data, {bool forGrid = false}) async {
    if (data != null && _work != null) {
       String site = data["site"] ?? "";
       String attachmentId = data["attachmentId"] ?? "";
       String param = data["param"] ?? "";
       OLogger.d('替换附件 site $site , attachmentId $attachmentId , param $param');
       O2Utils.pickerFileOrImage((paths) {
        _replaceAttachmentBegin(paths, site, param, attachmentId, forGrid);
       });
    }
  }

  void _replaceAttachmentBegin(List<String?> paths, String site, String param, String attachmentId, bool forGrid) async {
    if (paths.isEmpty) {
      return;
    }
    String path = paths[0] ?? '';
    if (path.isEmpty) {
      return;
    }
    Loading.show();
    OLogger.d('选择了文件：$path');
    var resId = await ProcessSurfaceService.to.replaceAttachment(_work!.id!, attachmentId, File(path));
    if (resId != null && resId.id != null) {
      if (forGrid) {
        webviewController?.evaluateJavascript(source: "layout.app.appForm.replacedAttachmentDatagrid(\"$site\", \"${resId.id}\", \"$param\")");
      } else {
        webviewController?.evaluateJavascript(source: "layout.app.appForm.replacedAttachment(\"$site\", \"${resId.id}\")");
      }
      OLogger.d('替换附件成功 ${resId.id} ');
      Loading.dismiss();
    }
    
  }

  ///
  /// 打开公文
  /// 
  void _openDocument(Map<String, dynamic>? data) async {
    OLogger.d('打开公文！！！');
    if (data != null) {
      String url = data['url'] ?? '';
      if (url.isNotEmpty) {
        Loading.show();
        var dir = await O2FilePathUtil.getTempFolderWithUrl(url);
        if (dir == null || dir.isEmpty) {
          OLogger.e('本地文件目录生成失败！ url: $url');
          Loading.showError('下载失败！');
          return;
        }
        try {
          var filePath = await O2HttpClient.instance.downloadFileNoFileName(url, dir);
          Loading.dismiss();
          OLogger.d('文件地址 $filePath');
          // _openAttachmentFile(filePath, info.name!);
        } on Exception catch (e) {
          Loading.showError(e.toString());
        }
      }
    }
  }

  ///
  /// 下载附件
  ///
  void _downloadAttachment(Map<String, dynamic>? data) {
    OLogger.d("下载附件");
    if (data != null) {
      String attachmentId = data["attachmentId"] ?? "";
      if (attachmentId.isNotEmpty && _work != null) {
        if (_work!.isWorkCompleted()) {
          _downloadWorkcompletedAttachment(_work!.id!, attachmentId);
        } else {
          _downloadWorkAttachment(_work!.id!, attachmentId);
        }
      }
    }
  }
 

  ///
  /// 下载附件
  ///
  void _downloadWorkcompletedAttachment(
      String workcompletedId, String attachmentId) async {
    Loading.show();
    // 请求附件对象信息
    WorkAttachmentInfo? info = await ProcessSurfaceService.to
        .getAttachmentInfoWithWorkcompletedId(workcompletedId, attachmentId);
    if (info != null && info.name != null && info.id != null) {
      String? filePath = await O2FilePathUtil.getProcessFileDownloadLocalPath(info.id!, info.name!);
      if (filePath == null || filePath.isEmpty) {
        Loading.showError('process_work_download_file_no_path'.tr);
        return;
      }
      var file = File(filePath);
      if (file.existsSync()) {
        Loading.dismiss();
        _openAttachmentFile(filePath, info.name!);
      } else {
        // 下载附件
        var donwloadUrl = ProcessSurfaceService.to
            .downloadUrlWithWorkcompleted(workcompletedId, attachmentId);
        try {
          await O2HttpClient.instance.downloadFile(donwloadUrl, filePath);
          Loading.dismiss();
          _openAttachmentFile(filePath, info.name!);
        } on Exception catch (e) {
          Loading.showError(e.toString());
        }
      }
    } else {
      Loading.showError('process_work_download_get_attachmentinfo_fail'.tr);
    }
  }

  ///
  /// 下载附件
  ///
  void _downloadWorkAttachment(String workId, String attachmentId) async {
    Loading.show();
    // 请求附件对象信息
    WorkAttachmentInfo? info = await ProcessSurfaceService.to
        .getAttachmentInfoWithWorkid(workId, attachmentId);
    if (info != null && info.name != null && info.id != null) {
      String? filePath = await O2FilePathUtil.getProcessFileDownloadLocalPath(info.id!, info.name!);
      if (filePath == null || filePath.isEmpty) {
        Loading.showError('process_work_download_file_no_path'.tr);
        return;
      }
      var file = File(filePath);
      if (file.existsSync()) {
        Loading.dismiss();
        _openAttachmentFile(filePath, info.name!);
      } else {
        // 下载附件
        var donwloadUrl = ProcessSurfaceService.to
            .downloadUrlWithWorkId(workId, attachmentId);
        try {
          await O2HttpClient.instance.downloadFile(donwloadUrl, filePath);
          Loading.dismiss();
          _openAttachmentFile(filePath, info.name!);
        } on Exception catch (e) {
          Loading.showError(e.toString());
        }
      }
    } else {
      Loading.showError('process_work_download_get_attachmentinfo_fail'.tr);
    }
  }
 

  ///
  /// 打开附件
  ///
  void _openAttachmentFile(String filePath, String fileName) {
    if (filePath.isImageFileName) {
      PreviewImagePage.open(filePath, fileName);
    } else if (filePath.isVideoFileName) {
      VideoPlayerPage.openLocalVideo(filePath, title: fileName);
    } else {
      channel.openLocalFile(filePath);
    }
  }

  ///
  /// 工作表单加载完成
  ///
  void _appFormLoaded(Map<String, dynamic>? data) {
    OLogger.d("表单加载完成回调");
    if (data != null) {
      String j = json.encode(data);
      OLogger.i("传入对象：$j");
    }
  }

  ///
  /// 打开工作
  /// workId: String, workCompletedId: String, title: String
  void _openWork(Map<String, dynamic>? data) {
    if (data != null) {
      String workid = data['workId'] ?? '';
      if (workid.isEmpty) {
        workid = data['workCompletedId'] ?? '';
      }
      if (workid.isNotEmpty) {
        ProcessWebviewPage.open(workid,
            title: data['title'] ?? 'process_work_no_title_no_process'.tr);
      }
    }
  }
 

  ///
  /// 如果有callback函数 就执行
  ///
  void _executeCallbackJs(String? callback, dynamic result) {
    if (callback != null && callback.isNotEmpty) {
      if (result != null) {
        webviewController?.evaluateJavascript(source: '$callback($result)');
      } else {
        webviewController?.evaluateJavascript(source: '$callback()');
      }
    }
  }


  /// 复制当前链接
  void copyLink() async {
    final url = await webviewController?.getUrl();
    if (url != null) {
      Clipboard.setData(ClipboardData(text: url.toString()));
      Loading.toast('im_chat_success_copy'.tr);
    }
  }
  
}
